
package app;

import java.awt.*;
import java.util.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

/**
 * Główne okno aplikacji
 * Zawiera panel umożliwiający urzytkownikowi komunikację z programem oraz
 * panel przedstawiający trasę wraz z autami na niej.
 * 
 * @author morohusky
 */
public class MainForm extends javax.swing.JFrame {
    private final String StringEmpty = "";
    private final String TRACKS_FOLDER_NAME = "Tracks";
    private final String TRACK_FILE_FORMAT;
    private final String TRACKS_PATH;
    private final String MAIN_PATH;
    private Track _track;
    private Track _minTrack;
    private Graphics2D graphics;
    private boolean _isEditingTrack = false;
    private Stroke _stdStroke;
    private int _strokeSize;
    private Stroke _thickStroke;
    private final Stroke _minStroke = new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
    private final Play _game = new Play();
    private boolean[] _isCreatingEnabled;
    private final TrackFile _trackFile = new TrackFile();
    
    
    /**
     * Tworzy nowy obiekt klasy MainForm
     */
    public MainForm() {
        File file = new File("");
        MAIN_PATH = file.getAbsolutePath() + "\\";
        TRACKS_PATH = MAIN_PATH + TRACKS_FOLDER_NAME + "\\";
        TRACK_FILE_FORMAT = TRACKS_PATH + "%s.trk";
        
        initComponents();
        
        configureScreen();
        configureComponents();
        configureTracksFolder();
        configureListeners();
    }
    
    
    /**
     * Dopasowuje okno do ekrau użytkownika
     */
    private void configureScreen() {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] gs = ge.getScreenDevices();
        
        try {
            DisplayMode dm = gs[0].getDisplayMode();
            setSize(dm.getWidth(), dm.getHeight());
        }
        catch (Exception e) { System.err.println(e.getStackTrace()); }
    }
    
    /**
     * Ustawia początkowe wartości szerokości trasy oraz ustawia auta na
     * odpowiednie pozycje
     */
    private void configureComponents() {
        _strokeSize = sldTrackWidth.getValue();
        _thickStroke = new BasicStroke(_strokeSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
        
        panTrack.setLayout(null);
        ArrayList<JLabel> cars = _game.getCars();
        for (JLabel car : cars) {
            car.setVisible(false);
            panTrack.add(car);
        }
        panTrack.setComponentZOrder(lblTrack, cars.size());
    }
    
    /**
     * Tworzy folder z trasami jeśli nie istnieje
     */
    private void configureTracksFolder() {
        File file = new File(TRACKS_PATH);
        if (!file.exists()) {
            file.mkdir();
        }
        else {
            findTracks();
        }
    }
    
    /**
     * Ustawia słuchaczy myszki oraz klawiatury
     */
    private void configureListeners() {
        panTrack.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (_isEditingTrack)
                {
                    makeTrackPoint(e.getPoint());
                }
            }
        });
        
        Controller cont = _game.getController();
        addKeyListener(cont);
        for (Component comp : getComponents()) {
            comp.addKeyListener(cont);
        }
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        panNavi = new javax.swing.JPanel();
        panScrollWrapper = new javax.swing.JScrollPane();
        panModifications = new javax.swing.JPanel();
        lblMiniMap = new javax.swing.JLabel();
        panCreating = new javax.swing.JPanel();
        btnCreateTrack = new javax.swing.JButton();
        btnFinishTrack = new javax.swing.JButton();
        txtTrackName = new java.awt.TextField();
        lblTrackName = new javax.swing.JLabel();
        btnSave = new javax.swing.JButton();
        lstTracks = new java.awt.Choice();
        lblLoadName = new javax.swing.JLabel();
        btnLoad = new javax.swing.JButton();
        panControls = new javax.swing.JPanel();
        lblControls = new javax.swing.JLabel();
        btnRun = new javax.swing.JButton();
        btnStop = new javax.swing.JButton();
        panDiff = new javax.swing.JPanel();
        lblTrackWidth = new javax.swing.JLabel();
        sldTrackWidth = new javax.swing.JSlider();
        panMiniMapWrapper = new javax.swing.JPanel();
        canvMiniMap = new java.awt.Canvas();
        btnClose = new javax.swing.JButton();
        panTrack = new javax.swing.JPanel();
        lblTrack = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Sztuczna inteligencja");
        setName("MainForm"); // NOI18N
        setUndecorated(true);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        panNavi.setBackground(new java.awt.Color(153, 255, 153));

        panModifications.setBackground(panNavi.getBackground());
        panModifications.setEnabled(false);

        lblMiniMap.setText("Minimapa:");

        panCreating.setBackground(panNavi.getBackground());

        btnCreateTrack.setText("Stwórz trasę");
        btnCreateTrack.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnCreateTrackActionPerformed(evt);
            }
        });

        btnFinishTrack.setText("Zakończ trasę");
        btnFinishTrack.setEnabled(false);
        btnFinishTrack.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnFinishTrackActionPerformed(evt);
            }
        });

        txtTrackName.setName(""); // NOI18N

        lblTrackName.setText("Nazwa trasy:");

        btnSave.setText("Zapisz");
        btnSave.setEnabled(false);
        btnSave.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnSaveActionPerformed(evt);
            }
        });

        lstTracks.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                lstTracksItemStateChanged(evt);
            }
        });

        lblLoadName.setText("Trasa do wczytania:");

        btnLoad.setText("Wczytaj");
        btnLoad.setEnabled(false);
        btnLoad.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnLoadActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout panCreatingLayout = new javax.swing.GroupLayout(panCreating);
        panCreating.setLayout(panCreatingLayout);
        panCreatingLayout.setHorizontalGroup(
            panCreatingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(btnCreateTrack, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(btnFinishTrack, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(panCreatingLayout.createSequentialGroup()
                .addComponent(txtTrackName, javax.swing.GroupLayout.PREFERRED_SIZE, 113, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(btnSave, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            .addGroup(panCreatingLayout.createSequentialGroup()
                .addComponent(lstTracks, javax.swing.GroupLayout.PREFERRED_SIZE, 113, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(btnLoad, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            .addGroup(panCreatingLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(panCreatingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lblLoadName)
                    .addComponent(lblTrackName))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        panCreatingLayout.setVerticalGroup(
            panCreatingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panCreatingLayout.createSequentialGroup()
                .addComponent(btnCreateTrack)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(btnFinishTrack)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(lblTrackName)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(panCreatingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(btnSave, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(txtTrackName, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGap(9, 9, 9)
                .addComponent(lblLoadName)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(panCreatingLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lstTracks, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(btnLoad, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        panControls.setBackground(panNavi.getBackground());

        lblControls.setText("Sterowanie:");

        btnRun.setText("Rozpocznij");
        btnRun.setEnabled(false);
        btnRun.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnRunActionPerformed(evt);
            }
        });

        btnStop.setText("Koniec");
        btnStop.setEnabled(false);
        btnStop.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnStopActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout panControlsLayout = new javax.swing.GroupLayout(panControls);
        panControls.setLayout(panControlsLayout);
        panControlsLayout.setHorizontalGroup(
            panControlsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(btnRun, javax.swing.GroupLayout.DEFAULT_SIZE, 205, Short.MAX_VALUE)
            .addComponent(btnStop, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(panControlsLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(lblControls)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        panControlsLayout.setVerticalGroup(
            panControlsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panControlsLayout.createSequentialGroup()
                .addComponent(lblControls)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(btnRun)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(btnStop)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        panDiff.setBackground(panNavi.getBackground());

        lblTrackWidth.setText("Szerokość trasy:");

        sldTrackWidth.setMinimum(50);
        sldTrackWidth.setValue(100);
        sldTrackWidth.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                sldTrackWidthStateChanged(evt);
            }
        });

        javax.swing.GroupLayout panDiffLayout = new javax.swing.GroupLayout(panDiff);
        panDiff.setLayout(panDiffLayout);
        panDiffLayout.setHorizontalGroup(
            panDiffLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(sldTrackWidth, javax.swing.GroupLayout.DEFAULT_SIZE, 205, Short.MAX_VALUE)
            .addGroup(panDiffLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(lblTrackWidth)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        panDiffLayout.setVerticalGroup(
            panDiffLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panDiffLayout.createSequentialGroup()
                .addComponent(lblTrackWidth)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(sldTrackWidth, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 11, Short.MAX_VALUE))
        );

        panMiniMapWrapper.setBackground(panNavi.getBackground());
        panMiniMapWrapper.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        panMiniMapWrapper.setPreferredSize(new java.awt.Dimension(205, 137));

        canvMiniMap.setBackground(panNavi.getBackground());

        javax.swing.GroupLayout panMiniMapWrapperLayout = new javax.swing.GroupLayout(panMiniMapWrapper);
        panMiniMapWrapper.setLayout(panMiniMapWrapperLayout);
        panMiniMapWrapperLayout.setHorizontalGroup(
            panMiniMapWrapperLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panMiniMapWrapperLayout.createSequentialGroup()
                .addComponent(canvMiniMap, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 0, Short.MAX_VALUE))
        );
        panMiniMapWrapperLayout.setVerticalGroup(
            panMiniMapWrapperLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panMiniMapWrapperLayout.createSequentialGroup()
                .addComponent(canvMiniMap, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 3, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout panModificationsLayout = new javax.swing.GroupLayout(panModifications);
        panModifications.setLayout(panModificationsLayout);
        panModificationsLayout.setHorizontalGroup(
            panModificationsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(panCreating, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(panControls, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(panDiff, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(panModificationsLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(panModificationsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(panMiniMapWrapper, javax.swing.GroupLayout.DEFAULT_SIZE, 185, Short.MAX_VALUE)
                    .addGroup(panModificationsLayout.createSequentialGroup()
                        .addComponent(lblMiniMap)
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        panModificationsLayout.setVerticalGroup(
            panModificationsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panModificationsLayout.createSequentialGroup()
                .addComponent(panCreating, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(lblMiniMap)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(panMiniMapWrapper, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(panControls, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(panDiff, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        panScrollWrapper.setViewportView(panModifications);

        btnClose.setText("Zamknij");
        btnClose.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnCloseActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout panNaviLayout = new javax.swing.GroupLayout(panNavi);
        panNavi.setLayout(panNaviLayout);
        panNaviLayout.setHorizontalGroup(
            panNaviLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panNaviLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(panNaviLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(panScrollWrapper)
                    .addComponent(btnClose, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        panNaviLayout.setVerticalGroup(
            panNaviLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panNaviLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(panScrollWrapper, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 123, Short.MAX_VALUE)
                .addComponent(btnClose)
                .addContainerGap())
        );

        panTrack.setForeground(new java.awt.Color(153, 153, 153));

        lblTrack.setBackground(panTrack.getBackground());
        lblTrack.setForeground(panTrack.getForeground());
        lblTrack.setDoubleBuffered(true);

        javax.swing.GroupLayout panTrackLayout = new javax.swing.GroupLayout(panTrack);
        panTrack.setLayout(panTrackLayout);
        panTrackLayout.setHorizontalGroup(
            panTrackLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(lblTrack, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 750, Short.MAX_VALUE)
        );
        panTrackLayout.setVerticalGroup(
            panTrackLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panTrackLayout.createSequentialGroup()
                .addComponent(lblTrack, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(panTrack, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(panNavi, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(panNavi, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(panTrack, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents
    
    /**
     * Tworzy punkt trasy i odrysowuje trasę
     * @param point Nowy punkt na trasie
     */
    private void makeTrackPoint(Point point) {
        if (_track.addPoint(point)) {
            graphics.setColor(Color.black);
            graphics.drawOval(point.x - 3, point.y - 3, 6, 6);
            
            _stdStroke = graphics.getStroke();
            graphics.setStroke(_thickStroke);
            graphics.draw(_track.getActualPath());
            graphics.setStroke(_stdStroke);
            
            if (_track.getPointsCount() > 2) {
                btnFinishTrack.setEnabled(true);
            }
        }
    }
    
    /**
     * Odrysowuje trasę na podanym elemencie
     * @param track Trasa do odrysowania
     * @param g Obszar, na którym odrysowana będzie trasa
     * @param size Rozmiar obszaru
     * @param miniMap Zmienna określająca rodzaj trasy
     * @return Ścieżka graficzna przedstawiająca trasę
     */
    private GeneralPath drawTrack(Track track, Graphics2D g, Dimension size, boolean miniMap) {
        g.setColor(g.getBackground());
        g.fillRect(0, 0, size.width, size.height);
        
        GeneralPath path = new GeneralPath();
        
        int strokeSize = _strokeSize;
        if (miniMap) {
            strokeSize = 3;
        }
        
        track.fitTo(size, strokeSize);
        Point first = track.getPoint(0);
        path.moveTo(first.x, first.y);
        
        for (int i = 1; i < track.getPointsCount(); i++) {
            Point p = track.getPoint(i);
            path.lineTo(p.x, p.y);
        }
        
        path.closePath();
        path.setWindingRule(GeneralPath.WIND_EVEN_ODD);
        
        _stdStroke = g.getStroke();
        if (miniMap) {
            g.setColor(Color.red);
            g.setStroke(_minStroke);
        }
        else {
            g.setColor(Color.gray);
            g.setStroke(_thickStroke);
        }
        g.draw(path);
        g.setStroke(_stdStroke);
        g.setColor(Color.black);
        
        return path;
    }
    
    /**
     * Startuje poruszanie się obiektów
     */
    public void start() {
        _game.start(_track, _strokeSize);
    }
    
    /**
     * Zatrzymuje poruszanie się obiektów
     */
    public void stop() {
        _game.stop();
    }
    
    
    private void btnCreateTrackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCreateTrackActionPerformed
        _track = new Track();
        graphics.clearRect(0, 0, panTrack.getSize().width, panTrack.getSize().height);
        lblTrack.setVisible(false);
        _isEditingTrack = true;
    }//GEN-LAST:event_btnCreateTrackActionPerformed

    private void btnFinishTrackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFinishTrackActionPerformed
        if (!_track.closeTrack()) {
            javax.swing.JOptionPane.showMessageDialog(this, "Trasa powinna zawierać co najmniej 3 punkty, aby można było ją poprawnie utworzyć.",
                    "Nie można dokończyć trasy", javax.swing.JOptionPane.ERROR_MESSAGE);
            return;
        }
        
        btnSave.setEnabled(true);
        btnFinishTrack.setEnabled(false);
        showTrack();
        
        _isEditingTrack = false;
        
        drawTrack(_track, (Graphics2D)lblTrack.getGraphics(), lblTrack.getSize(), false);
        btnRun.setEnabled(_track != null);
    }//GEN-LAST:event_btnFinishTrackActionPerformed

    private void sldTrackWidthStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sldTrackWidthStateChanged
        _strokeSize = sldTrackWidth.getValue();
        _thickStroke = new BasicStroke(_strokeSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
    }//GEN-LAST:event_sldTrackWidthStateChanged

    private void btnSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSaveActionPerformed
        String name = txtTrackName.getText();
         
        if (name.length() != 0) {
            String filePath = String.format(TRACK_FILE_FORMAT, name);
            File file = new File(filePath);
            if (file.exists()) {
            JOptionPane.showMessageDialog(this, "Nazwa zapisywanej trasy już istnieje. Podaj nową nazwę.",
                "Plik już istnieje!", JOptionPane.ERROR_MESSAGE);
            }
            else {
                btnSave.setEnabled(false);
                _track.save(filePath);
                txtTrackName.setText(StringEmpty);
                findTracks();
            }
        }
        else {
            JOptionPane.showMessageDialog(this, "Nazwa zapisywanej trasy musi zawierać przynajmniej 1 znak.",
                "Nie zapisano trasy!", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_btnSaveActionPerformed

    private void btnLoadActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLoadActionPerformed
        _track = null;
        btnSave.setEnabled(false);
        btnFinishTrack.setEnabled(false);
        _isEditingTrack = false;
        String name = (String)lstTracks.getSelectedItem();

        _track = Track.load(String.format(TRACK_FILE_FORMAT, name));
        if (_track != null) {
            showTrack();
        }
        btnRun.setEnabled(_track != null);
    }//GEN-LAST:event_btnLoadActionPerformed

    private void lstTracksItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_lstTracksItemStateChanged
        if (lstTracks.getSelectedIndex() == 0) {
            btnLoad.setEnabled(false);
            canvMiniMap.getGraphics().clearRect(0, 0, canvMiniMap.getWidth(), canvMiniMap.getHeight());
            _minTrack = null;
        }
        else {
            loadMiniMap();
            btnLoad.setEnabled(true);
        }
    }//GEN-LAST:event_lstTracksItemStateChanged

    private void btnRunActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRunActionPerformed
        if (btnStop.isEnabled()) {
            btnRun.setEnabled(false);
            start();
        }
        else {
            requestFocus();
            setCreatingEnabled(false);
            btnRun.setEnabled(false);
            _game.setCarsVisible(true);
            start();
            btnStop.setEnabled(true);
        }
    }//GEN-LAST:event_btnRunActionPerformed

    private void btnStopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStopActionPerformed
        btnStop.setEnabled(false);
        stop();
        _game.setCarsVisible(false);
        _game.centerCarsOn(panTrack.getSize());
        btnRun.setEnabled(true);
        setCreatingEnabled(true);
    }//GEN-LAST:event_btnStopActionPerformed

    private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
        graphics = (Graphics2D)panTrack.getGraphics();
        lblTrack.setSize(panTrack.getWidth(), panTrack.getHeight());
        
        _game.centerCarsOn(panTrack.getSize());
    }//GEN-LAST:event_formWindowOpened

    private void btnCloseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCloseActionPerformed
        System.exit(0);
    }//GEN-LAST:event_btnCloseActionPerformed
    
    /**
     * Blokuje lub odblokowuje możliwość tworzenia trasy
     * @param isEnabled Określenie możliwości tworzenia trasy
     */
    private void setCreatingEnabled(boolean isEnabled) {
        if (isEnabled) {
            int i = 0;
            for (Component comp : panCreating.getComponents()) {
                comp.setEnabled(_isCreatingEnabled[i++]);
            }
        }
        else {
            _isCreatingEnabled = new boolean[panCreating.getComponentCount()];
            int i = 0;
            for (Component comp : panCreating.getComponents()) {
                _isCreatingEnabled[i++] = comp.isEnabled();
                comp.setEnabled(false);
            }
        }
    }
    
    /**
     * Wyświetlenie trasy użytkownikowi
     */
    private void showTrack() {
        BufferedImage img = new BufferedImage(lblTrack.getWidth(), lblTrack.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        Graphics2D g = (Graphics2D)img.getGraphics();
        g.setBackground(panTrack.getBackground());
        drawTrack(_track, g, lblTrack.getSize(), false);

        lblTrack.setIcon(new ImageIcon(img));
        lblTrack.setVisible(true);
    }
    
    /**
     * Wczytanie wszystkich tras z odpowiedniego folderu
     */
    private void findTracks() {
        lstTracks.removeAll();
        lstTracks.add(StringEmpty);
        lstTracksItemStateChanged(null);
        btnLoad.setEnabled(false);
        
        ArrayList<String> tracks = _trackFile.findTracks(TRACKS_PATH);
        for (String trackName : tracks) {
            lstTracks.add(trackName);
        }
        
        lstTracks.select(0);
    }
    
    /**
     * Wświetlenie użytkownikowi pomniejszonej trasy (podglądu)
     */
    public void loadMiniMap(){
        String minName = lstTracks.getSelectedItem();
        _minTrack = Track.load(String.format(TRACK_FILE_FORMAT, minName));
        drawTrack(_minTrack, (Graphics2D)canvMiniMap.getGraphics(), canvMiniMap.getSize(), true);
    }    

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new MainForm().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnClose;
    private javax.swing.JButton btnCreateTrack;
    private javax.swing.JButton btnFinishTrack;
    private javax.swing.JButton btnLoad;
    private javax.swing.JButton btnRun;
    private javax.swing.JButton btnSave;
    private javax.swing.JButton btnStop;
    private java.awt.Canvas canvMiniMap;
    private javax.swing.JLabel lblControls;
    private javax.swing.JLabel lblLoadName;
    private javax.swing.JLabel lblMiniMap;
    private javax.swing.JLabel lblTrack;
    private javax.swing.JLabel lblTrackName;
    private javax.swing.JLabel lblTrackWidth;
    public static java.awt.Choice lstTracks;
    private javax.swing.JPanel panControls;
    private javax.swing.JPanel panCreating;
    private javax.swing.JPanel panDiff;
    private javax.swing.JPanel panMiniMapWrapper;
    private javax.swing.JPanel panModifications;
    private javax.swing.JPanel panNavi;
    private javax.swing.JScrollPane panScrollWrapper;
    private javax.swing.JPanel panTrack;
    private javax.swing.JSlider sldTrackWidth;
    private java.awt.TextField txtTrackName;
    // End of variables declaration//GEN-END:variables
    
}
